Embora o NumPy seja construído em C, certos algoritmos intensivos em cálculo atingem uma parede de vetorização. Isso ocorre quando a latência intrínseca da natureza dinâmica do Python supera os benefícios da abstração de alto nível.
1. A Taxa do Interpretador e Embalagem
Cada iteração em um loop padrão do Python envolve verificação dinâmica de tipos e contagem de referências. Mesmo ao usar escalares do NumPy, a "embalagem" dos dados brutos em C em objetos do Python cria um gargalo significativo para funções como $\text{logit}(p) = \log(p/(1-p))$. Tratar casos extremos em C é drasticamente mais rápido:
>>> logit(0) -> -inf >>> logit(1) -> inf >>> logit(2) -> nan >>> logit(-2) -> nan
2. Inchaço de Matrizes Intermediárias
Expressões puras do NumPy criam buffers de memória temporários para cada operação secundária. Estender via a API do C permite Fusão de Núcleo, onde a transformação logit é calculada em uma única passagem sem sobrecarga adicional de memória.
3. Dependências Espaciais
Operações que envolvem padrões de acesso a vizinhos, como o estêncil 2D:
$$B(I, J) = A(I, J) + (A(I-1, J) + A(I+1, J) + A(I, J-1) + A(I, J+1)) \cdot 0.5D0 + (A(I-1, J-1) + A(I-1, J+1) + A(I+1, J-1) + A(I+1, J+1)) \cdot 0.25D0$$
são difíceis de expressar de forma eficiente por meio de fatiamento sem cópias redundantes de memória. Extensões em C permitem aritmética direta de ponteiros alinhados ao cache.